// 包含交易类相关的头文件，用于创建交易对象和执行交易操作
#include <Trade/Trade.mqh>
// 包含获取交易品种信息的头文件，用于获取交易品种的各种数据
#include <Trade/SymbolInfo.mqh>

#define STOPLOSS 3//止损
#define DIFFER 0.7//差值
#define DIFFER1 1//差值
#define BIGDIFFER 0.5//开价差
#define TAKE_PROFIT 55  // 盈利点数  21 55   20 55  20 50
#define STOP_LOSS 20    // 止损点数

#define PERIOD PERIOD_M15

double TOP_price;      //多单价格
double MINI_price;     //空单价格
int order_flage;  //订单标志位0空1多

// 用于存储多单和空单的订单号
int longOrderTicket = 0;//多
int shortOrderTicket = 0;//空

int next = 0;

//--- 设置交易品种
// string 是 MQL5 中的字符串类型，symbol 变量用于存储要交易的品种名称
// "GOLD" 表示黄金交易品种
string symbol = "GOLD";

//--- 设置手数
// double 是 MQL5 中的双精度浮点数类型，lot 变量用于存储交易的手数
// 0.01 表示本次交易的手数为 0.01 手
double lot = 0.01;

//--- 创建交易类对象
// CTrade 是 MQL5 中用于执行交易操作的类，创建一个名为 trade 的 CTrade 对象
CTrade trade;

// 新增函数：平掉所有指定品种和指定类型的订单
void CloseAllPositions(int positionType) {
    int positionCount = PositionsTotal();
    bool hasClosed = false;

    for (int pos = positionCount - 1; pos >= 0; pos--) {
        ulong positionTicket = PositionGetTicket(pos);
        if (PositionGetString(POSITION_SYMBOL) == symbol) {
            int currentPositionType = PositionGetInteger(POSITION_TYPE);
            if ((positionType == 0 && currentPositionType == POSITION_TYPE_SELL) || 
                (positionType == 1 && currentPositionType == POSITION_TYPE_BUY)) {
                double price;
                if (currentPositionType == POSITION_TYPE_BUY) {
                    price = SymbolInfoDouble(symbol, SYMBOL_BID);
                } else {
                    price = SymbolInfoDouble(symbol, SYMBOL_ASK);
                }
                if (trade.PositionClose(positionTicket, price)) {
                    Print("订单 " + IntegerToString(positionTicket) + " 平仓成功");
                    hasClosed = true;
                } else {
                    Print("订单 " + IntegerToString(positionTicket) + " 平仓失败，错误代码: " + IntegerToString(trade.ResultRetcode()));
                    Print("错误描述: " + trade.ResultComment());
                }
            }
        }
    }

    if (positionType == 0) {
        if (hasClosed) {
            Print("空单已平掉");
        } else {
            Print("没有可平的空单");
        }
    } else if (positionType == 1) {
        if (hasClosed) {
            Print("多单已平掉");
        } else {
            Print("没有可平的多单");
        }
    }
}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
// OnInit 函数是专家顾问（EA）初始化时调用的函数，返回一个整数表示初始化结果
#define HISTORY_TIME 10  // 宏定义历史时间点，这里表示往前数第 10 根 K 线

int OnInit()
{
// 获取历史时间点的现价
// 获取历史时间点的现价
double historicalPrice = iClose(symbol, PERIOD_CURRENT, HISTORY_TIME);
// 获取历史 K 线的起始时间
datetime historicalTime = iTime(symbol, PERIOD_CURRENT, HISTORY_TIME);
// 将时间戳转换为字符串格式（年月日时分）
string timeString = TimeToString(historicalTime, TIME_DATE | TIME_SECONDS);

Print("历史时间点（往前数第 ", HISTORY_TIME, " 根 K 线）的时间: ", timeString);
Print("历史时间点（往前数第 ", HISTORY_TIME, " 根 K 线）的现价: ", historicalPrice);

/*
    //--- 获取当前市场价格
    // SymbolInfoDouble 是一个函数，用于获取指定交易品种的双精度浮点型信息
    // 第一个参数 symbol 是要查询的交易品种名称，这里是 "GOLD"
    // 第二个参数 SYMBOL_ASK 是一个常量，表示要获取的是该品种的卖价（即当前市场上可以买入该品种的价格）
    // 返回值存储在 buyPrice 变量中，为双精度浮点数类型
    double buyPrice = SymbolInfoDouble(symbol, SYMBOL_ASK);

    //--- 执行买入操作
    // trade.Buy 是 CTrade 类的成员函数，用于执行买入交易
    // 第一个参数 lot 是交易的手数，这里是 0.01 手
    // 第二个参数 symbol 是要交易的品种名称，这里是 "GOLD"
    // 第三个参数 buyPrice 是交易的价格，这里是之前获取的当前市场卖价
    if (trade.Buy(lot, symbol, buyPrice)) {
        longOrderTicket = trade.ResultDeal();
        Print("第一次做多交易成功，订单号: ", longOrderTicket);
		TOP_price = buyPrice;
    } else {
        Print("第一次做多交易失败，错误代码: ", trade.ResultRetcode());
        Print("错误描述: ", trade.ResultComment());
    }

    //--- 获取当前市场价格（用于做空）
    // 这里获取卖价（用于做多）和买价（用于做空）
    double sellPrice = SymbolInfoDouble(symbol, SYMBOL_BID);

    //--- 执行卖出操作
    // trade.Sell 是 CTrade 类的成员函数，用于执行卖出交易
    // 第一个参数 lot 是交易的手数，这里是 0.01 手
    // 第二个参数 symbol 是要交易的品种名称，这里是 "GOLD"
    // 第三个参数 sellPrice 是交易的价格，这里是之前获取的当前市场买价
    if (trade.Sell(lot, symbol, sellPrice)) {
        shortOrderTicket = trade.ResultDeal();
        Print("第二次做空交易成功，订单号: ", shortOrderTicket);
		MINI_price = sellPrice;
    } else {
        Print("第二次做空交易失败，错误代码: ", trade.ResultRetcode());
        Print("错误描述: ", trade.ResultComment());
    }

    // 判断是否有未平仓订单
    int positionCount = PositionsTotal();
    if (positionCount > 0) {
        Print("此时有单");
    } else {
        Print("现在没有单");
    }

    // 计算目前持有的黄金手数
    double totalLots = 0;
    for (int pos = 0; pos < positionCount; pos++) {
        ulong positionTicket = PositionGetTicket(pos);
        if (PositionGetString(POSITION_SYMBOL) == symbol) {
            totalLots += PositionGetDouble(POSITION_VOLUME);
        }
    }
    Print("目前持有的黄金手数: ", totalLots);


    // 示例：平掉所有空单
    CloseAllPositions(0);
	
	CloseAllPositions(1);

    //---
    // 返回 INIT_SUCCEEDED 表示专家顾问初始化成功
    */
    return(INIT_SUCCEEDED);
}

// 新增函数：检查并设置多单和空单标志，同时赋值开仓价格
void CheckAndSetOrderFlags() {
    int positionCount = PositionsTotal();
    bool hasLongPosition = false;
    bool hasShortPosition = false;

    TOP_price = 0;
    MINI_price = 0;

    for (int pos = 0; pos < positionCount; ++pos) {
        ulong positionTicket = PositionGetTicket(pos);
        if (PositionGetString(POSITION_SYMBOL) == symbol) {
            int positionType = PositionGetInteger(POSITION_TYPE);
            if (positionType == POSITION_TYPE_BUY) {
                hasLongPosition = true;
                double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
                if (openPrice > 0) {
                    TOP_price = openPrice;
                }
            } else if (positionType == POSITION_TYPE_SELL) {
                hasShortPosition = true;
                double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
                if (openPrice > 0) {
                    MINI_price = openPrice;
                }
            }
        }
    }

    longOrderTicket = hasLongPosition ? 1 : 0;
    shortOrderTicket = hasShortPosition ? 1 : 0;

    Print("longOrderTicket 已设置为: ", longOrderTicket);
    Print("shortOrderTicket 已设置为: ", shortOrderTicket);
    Print("TOP_price 已设置为: ", TOP_price);
    Print("MINI_price 已设置为: ", MINI_price);
}


//判断是否卖出
int PriceJudgment(void)
{

    int positionCount = PositionsTotal();
	
    if (positionCount == 0)
    {
		longOrderTicket = 0;//多
		shortOrderTicket = 0;//空
        Print("111111此时无单");
		return 1;
    }


	double buyPrice = SymbolInfoDouble(symbol, SYMBOL_ASK);
	double sellPrice = SymbolInfoDouble(symbol, SYMBOL_BID);

	if( (buyPrice - TOP_price) >= TAKE_PROFIT && longOrderTicket > 0 )//多止盈
	{
		CloseAllPositions(1);
		longOrderTicket = 0;
	}
	else if( (TOP_price - buyPrice) >= STOP_LOSS && longOrderTicket > 0 )//多止损
	{
		CloseAllPositions(1);
		longOrderTicket = 0;
	}
	
	if( (MINI_price - sellPrice) >= TAKE_PROFIT && shortOrderTicket > 0 )//空止盈
	{
		CloseAllPositions(0);
		shortOrderTicket = 0;
	}
	else if( (sellPrice - MINI_price) >= STOP_LOSS && shortOrderTicket > 0 )//多止损
	{
		CloseAllPositions(0);
		shortOrderTicket = 0;
	}

	if( longOrderTicket == 0 && shortOrderTicket == 0 )
	{
		printf("333333333333333333\r\n");
		return 1;
	}
	
	return 2;
}
//是否下单
int PlaceOrder(void)
{
	if( longOrderTicket > 0 || shortOrderTicket > 0 )
	{
		return 2;
	}
	
    //--- 获取当前市场价格
    // SymbolInfoDouble 是一个函数，用于获取指定交易品种的双精度浮点型信息
    // 第一个参数 symbol 是要查询的交易品种名称，这里是 "GOLD"
    // 第二个参数 SYMBOL_ASK 是一个常量，表示要获取的是该品种的卖价（即当前市场上可以买入该品种的价格）
    // 返回值存储在 buyPrice 变量中，为双精度浮点数类型
    double buyPrice = SymbolInfoDouble(symbol, SYMBOL_ASK);

    //--- 执行买入操作
    // trade.Buy 是 CTrade 类的成员函数，用于执行买入交易
    // 第一个参数 lot 是交易的手数，这里是 0.01 手
    // 第二个参数 symbol 是要交易的品种名称，这里是 "GOLD"
    // 第三个参数 buyPrice 是交易的价格，这里是之前获取的当前市场卖价
    if (trade.Buy(lot, symbol, buyPrice)) {
        longOrderTicket = trade.ResultDeal();
        Print("第一次做多交易成功，订单号: ", longOrderTicket);
		TOP_price = buyPrice;
    } else {
        Print("第一次做多交易失败，错误代码: ", trade.ResultRetcode());
        Print("错误描述: ", trade.ResultComment());
    }

    //--- 获取当前市场价格（用于做空）
    // 这里获取卖价（用于做多）和买价（用于做空）
    double sellPrice = SymbolInfoDouble(symbol, SYMBOL_BID);

    //--- 执行卖出操作
    // trade.Sell 是 CTrade 类的成员函数，用于执行卖出交易
    // 第一个参数 lot 是交易的手数，这里是 0.01 手
    // 第二个参数 symbol 是要交易的品种名称，这里是 "GOLD"
    // 第三个参数 sellPrice 是交易的价格，这里是之前获取的当前市场买价
    if (trade.Sell(lot, symbol, sellPrice)) {
        shortOrderTicket = trade.ResultDeal();
        Print("第二次做空交易成功，订单号: ", shortOrderTicket);
		MINI_price = sellPrice;
    } else {
        Print("第二次做空交易失败，错误代码: ", trade.ResultRetcode());
        Print("错误描述: ", trade.ResultComment());
    }

	return 2;//返回本身
}
int OrderDetermination(void)
{
    int positionCount = PositionsTotal();
	
    if (positionCount > 0)
    {
        Print("111111此时有单");
		CheckAndSetOrderFlags();
		return 2;
    }
    else
    {
        Print("222222现在没有单");
		longOrderTicket = 0;//多
		shortOrderTicket = 0;//空
		return 1;
    }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
// OnTick 函数是专家顾问（EA）在每个新的报价到来时调用的函数
void OnTick()
{

	//while(1)
	{
		//短暂休眠，避免CPU占用过高
		//printf("next == %d",next);
	    //Sleep(100);
		switch (next) 
		{
	        case 0:
		        next = OrderDetermination();//判断是否有单
		        break;
	        case 1:
	            next = PlaceOrder();//下单操作
	            break;
	        case 2:
	            next = PriceJudgment();//是否卖出
	            break;
	        case 3:
	           // next = WaitFor15MinuteKLineEnd();//结束这一根
	            break;
	        case 4:
	            printf("星期四\n");
	            break;
	        case 5:
	            printf("星期五\n");
	            break;
	        case 6:
	            printf("星期六\n");
	            break;
	        case 7:
	            printf("星期日\n");
	            break;
	        default:
	            printf("输入的数字不在1 - 7范围内\n");
	    }
	}
}

